package org.melanesia.converters.java;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;

import org.melanesia.converters.JavaToJavaConverter;
import org.melanesia.converters.TypeConverter;
import org.melanesia.converters.exceptions.ConversionException;
import org.melanesia.converters.exceptions.UnsupportedClassException;

/**
 * Converts java number class into another number class.
 *
 * @author marcin.kielar
 */
public class NumberToNumberConverter extends JavaToJavaConverter {

    /** The class that this converter converts to. */
    private final Class<?> toClass;

    /** Supported classes. */
    private static final Class<?>[] NUMBER_CLASSES = new Class<?>[] {
            Long.class, Integer.class, Short.class, Byte.class, Double.class,
            Float.class,
            long.class, int.class, short.class, byte.class, double.class,
            float.class,
            BigInteger.class, BigDecimal.class
    };

    /**
     * Returns an array of array of classes. The returned structure contains a
     * matrix of possible conversions.
     *
     * @return matrix of possible conversions.
     */
    public static Class<?>[][] getSupportedConversions() {

        ArrayList<Class<?>[]> list = new ArrayList<Class<?>[]>();

        for (Class<?> numberClass1 : NUMBER_CLASSES) {
            for (Class<?> numberClass2 : NUMBER_CLASSES) {
                if (!numberClass1.equals(numberClass2)) {
                    list.add(new Class<?>[] { numberClass1, numberClass2 });
                }
            }
        }

        Class<?>[][] array = new Class<?>[list.size()][];
        list.toArray(array);

        return array;
    }

    /**
     * Constructs the NumberToNumberConverter.
     *
     * @param toClass
     *            class this converter converts values to
     */
    public NumberToNumberConverter(final Class<?> toClass) {
        this.toClass = toClass;
    }

    @Override
    public final Object convert(final TypeConverter typeConverterContext, final Object value) throws ConversionException {
        if (value == null) {
            return null;
        } else if (toClass.equals(value.getClass())) {
            return value;
        } else {
            if (value instanceof Number) {
                if (Long.class.equals(toClass) || long.class.equals(toClass)) {
                    return ((Number) value).longValue();
                } else if (Integer.class.equals(toClass)
                        || int.class.equals(toClass)) {
                    return ((Number) value).intValue();
                } else if (Short.class.equals(toClass)
                        || short.class.equals(toClass)) {
                    return ((Number) value).shortValue();
                } else if (Byte.class.equals(toClass)
                        || byte.class.equals(toClass)) {
                    return ((Number) value).byteValue();
                } else if (Float.class.equals(toClass)
                        || float.class.equals(toClass)) {
                    return ((Number) value).shortValue();
                } else if (Double.class.equals(toClass)
                        || double.class.equals(toClass)) {
                    return ((Number) value).doubleValue();
                } else if (BigInteger.class.equals(toClass)) {
                    return BigInteger.valueOf(((Number) value).longValue());
                } else if (BigDecimal.class.equals(toClass)) {
                    return BigDecimal.valueOf(((Number) value).doubleValue());
                }
            }
        }

        throw new UnsupportedClassException(value.getClass());
    }
}
